package com.songshan.aspect;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.songshan.bean.User;
import com.songshan.filter.UserContext;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import java.util.*;

/**
 * @author feb
 * @date 2024/6/18
 * @Description: Service方法执行日志切面
 */
@Aspect
@Component
public class LogAspect {

  /**
   * @author feb
   * @date 2024/6/18
   * @Description: slf4j日志框架通用API 根据当前类信息打印日志信息
   */
  private Logger log = LoggerFactory.getLogger(LogAspect.class);

  /**
   * @author feb
   * @date 2024/6/18
   * @Description: 切点 通知所有service中的以add/mod/del为前缀的方法 通知所有带有@LogAdvice注解的方法
   */
  @Pointcut("(execution(* com.songshan.service.*.add*(..)))"
      + " || (execution(* com.songshan.service.*.mod*(..)))"
      + " || (execution(* com.songshan.service.*.del*(..)))"
      + " || (@annotation(com.songshan.aspect.LogAdvice))")
  public void pointCut() {

  }

  /**
   * @author feb
   * @date 2024/6/18
   * @Description: pointCut切点的前置通知
   */
  @Before("pointCut()")
  public void beforeAdvice(JoinPoint joinPoint) {
//    log.info("["+joinPoint.getSignature().getName()+"]前置处理...");
  }

  /**
   * @author feb
   * @date 2024/6/18
   * @Description: pointCut切点的环绕通知
   */
  @Around("pointCut()")
  public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
    long time = System.currentTimeMillis();
    String exceptions = null;
    Object result = null;
    try {
      result = joinPoint.proceed();
      return result;
    } catch (Throwable e) {
      exceptions = e.getMessage();
      throw e;
    } finally {
      //计算执行时间
      time = System.currentTimeMillis() - time;
      String times = time + "ms" + (time > 2000 ? "（业务操作超时）" : "");
      //当前登录用户
      User user = UserContext.get();
      String users = user == null ? "" : user.getName() + "(" + user.getUsername() + ")";
      //目标方法信息
      String className = joinPoint.getTarget().getClass().getName();
      String methodName = joinPoint.getSignature().getName();
      String methods = className + "." + methodName;
      //执行参数
      String params = formatParams(joinPoint.getArgs());
      //执行结果
      String results =
          exceptions != null ? exceptions : result == null ? "" : JSONObject.toJSONString(result);
      StringBuilder logInfo = new StringBuilder();
      logInfo.append("\n-------------------开始-----------------");
      logInfo.append("\nUSER : " + users);
      logInfo.append("\nMETHOD : " + methods);
      logInfo.append("\nPARAM : " + params);
      logInfo.append("\nRESULT : " + results);
      logInfo.append("\nTIME : " + times);
      logInfo.append("\n-------------------结束-----------------");
      log.info(logInfo.toString());
    }
  }

  private String formatParams(Object[] params){
    if(params==null||params.length==0) return "[]";
    List<Object> paramList = new ArrayList<>();
    for(Object param:params){
      if(param instanceof MultipartHttpServletRequest){
        Map<String,MultipartFile> fileMap = ((MultipartHttpServletRequest)param).getFileMap();
        if(fileMap==null||fileMap.isEmpty()){
          continue;
        }
        Map<String,String> fileNameMap = new HashMap<>();
        for(String key:fileMap.keySet()){
          fileNameMap.put(key,fileMap.get(key).getOriginalFilename());
        }
        paramList.add(fileNameMap);
      }else if(param instanceof MultipartFile){
        Map<String,String> fileNameMap = new HashMap<>();
        fileNameMap.put(((MultipartFile)param).getName(),((MultipartFile)param).getOriginalFilename());
        paramList.add(fileNameMap);
      }else{
        paramList.add(param);
      }
    }
    return JSONArray.toJSONString(paramList);
  }

  /**
   * @author feb
   * @date 2024/6/18
   * @Description: pointCut切点的返回通知
   */
  @AfterReturning(pointcut = "pointCut()", returning = "result")
  public void afterReturningAdvice(JoinPoint joinPoint, Object result) {
//    log.info("["+joinPoint.getSignature().getName()+"]返回结果处理："+ JSONObject.toJSONString(result));
  }

  /**
   * @author feb
   * @date 2024/6/18
   * @Description: pointCut切点的后置通知
   */
  @After("pointCut()")
  public void afterAdvice(JoinPoint joinPoint) {
//    log.info("["+joinPoint.getSignature().getName()+"]后置处理...");
  }

  /**
   * @author feb
   * @date 2024/6/18
   * @Description: pointCut切点的异常通知
   */
  @AfterThrowing(pointcut = "pointCut()", throwing = "exception")
  public void throwAdvice(JoinPoint joinPoint, Exception exception) {
//    log.info("["+joinPoint.getSignature().getName()+"]异常处理："+exception.getMessage());
  }
}
